home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 9 / Night Owl CD-ROM (NOPV9) (Night Owl Publisher) (1993).ISO / 054a / execwn.zip / EXECWIN.PAS < prev   
Pascal/Delphi Source File  |  1993-02-11  |  17KB  |  642 lines

  1. {
  2.  This unit interfaces one function, ExecWindow, which uses an Exec
  3.  routine to run a child process. In addition to what the Exec routine
  4.  does, ExecWindow attempts to keep the video output of the child
  5.  process within a specified window on the screen. This is useful for
  6.  some programs, as exemplified by the INSTALL.EXE program used for
  7.  installation of Turbo Professional files.
  8.  
  9.  The technique used is to grab interrupt 21h and thus control all
  10.  writes to the standard output and error devices. These are rerouted
  11.  to the screen, within the specified window. The technique used by
  12.  ExecWindow will not work for programs that write directly to video
  13.  memory, through the BIOS, or through some other file handle assigned
  14.  to the console. It does work with standard DOS commands, with the
  15.  Borland command line compilers, and with other command line utilities
  16.  like PKZIP and LHARC.
  17.  
  18.  Written by Kim Kokkonen, TurboPower Software
  19.  Released to the public domain
  20.  
  21.  10/09/88 - initial release
  22.  11/02/90 - update for TP6
  23.  11/30/90 - fix to work again with TP5.5
  24.  09/21/92 - update for protected mode operation under BP7
  25.             (no longer supports TP4)
  26.  01/26/93 - trap int 10 calls to support PKZIP 2.0
  27.             also fixes a problem with backspacing at DOS command line
  28.  02/11/93 - trap int 29 calls to support PKZIP 2.0 under fast ANSI drivers
  29. }
  30.  
  31. {$R-,S-,I-,B-,F-,V-}
  32.  
  33. unit ExecWin;
  34.   {-Exec a program in a window}
  35.  
  36. interface
  37.  
  38. function ExecWindow(Command : String;
  39.                     UseSecond : Boolean;
  40.                     Xlo, Ylo, Xhi, Yhi : Byte;
  41.                     Attr : Byte) : Integer;
  42.   {-Exec a program in a window.
  43.     Command contains the program name and command line parameters to
  44.       execute, similar to how you would enter them at the DOS command
  45.       line.
  46.     UseSecond is true if you want to execute COMMAND.COM, which will
  47.       execute your program in turn. UseSecond must be True if you want
  48.       COMMAND to search the path or if you need to use device
  49.       redirection. Note, however, that if UseSecond is True, the error
  50.       level returned by DOS.DosExitCode is that of COMMAND.COM rather
  51.       than the actual program being executed.
  52.     Xlo, Ylo, Xhi, Yhi are the window boundaries, just as for a
  53.       Window() call.
  54.     Attr is the video attribute for all writes within the window.
  55.     ExecWindow returns a DOS error code, plus the following additional
  56.       codes:
  57.         99:     window coordinates invalid
  58.         $8008:  insufficient DOS memory for int21 handler (DPMI only)
  59.         -1..-4: error code returned by Turbo Professional ExecDos
  60.                 (real mode only)
  61.     Note that ExecWindow calls SwapVectors for you. Do not call
  62.     SwapVectors in your own program prior to calling ExecWindow.
  63.   }
  64.  
  65.   {=======================================================================}
  66.  
  67. implementation
  68.  
  69. {$IFDEF DPMI}      {DOS protected mode version----------------------------}
  70.  
  71. uses
  72.   Dos, WinApi, Dpmi;
  73.  
  74. const
  75.   WindLoX = 0;     {Zero-relative offsets of window data in Int21 below}
  76.   WindLoY = 1;
  77.   WindHiX = 2;
  78.   WindHiY = 3;
  79.   WindPosX = 4;
  80.   WindPosY = 5;
  81.   WindAttr = 6;
  82.   Int21Patch = 9;  {Zero-relative offset of Int21 patch point}
  83.   Int10Patch = 14;
  84.   Int21Entry = 18; {Zero-relative offset of actual Int21 entry point}
  85.   Int29Entry = 20;
  86.   Int10Entry = 22;
  87.  
  88. {The int handlers are copied into real mode memory before activation}
  89. procedure IntHandlers; near; assembler;
  90. asm
  91. {CS-relative data}
  92.   db    0                       {WindLoX}
  93.   db    0                       {WindLoY}
  94.   db    0                       {WindHiX}
  95.   db    0                       {WindHiY}
  96.   db    0                       {WindPosX}
  97.   db    0                       {WindPosY}
  98.   db    0                       {WindAttr}
  99.  
  100. @JmpOld21:
  101.   cli                           {Interrupts back off}
  102.   db    $EA,0,0,0,0             {Jump to previous int21 vector}
  103.  
  104. @JmpOld10:
  105.   db    $EA,0,0,0,0             {Jump to previous int10 vector}
  106.  
  107. {Actual Int21 entry point here}
  108.   jmp   @OurInt21
  109.  
  110. {Actual Int29 entry point here}
  111.   jmp   @TtyOut
  112.  
  113. {Actual Int10 entry point here}
  114.   cmp   ah,$0E
  115.   je    @TtyOut
  116.   cmp   ah,$09
  117.   je    @MultOut
  118.   cmp   ah,$0A
  119.   je    @MultOut
  120.   cmp   ah,2
  121.   je    @SetCurPos
  122.   jmp   @JmpOld10
  123.  
  124. @OurInt21:
  125.   sti                           {Allow interrupts}
  126.   cmp   ah,2                    {Classify the DOS functions we care about}
  127.   je    @DispOut
  128.   cmp   ah,6
  129.   je    @DirectOut
  130.   cmp   ah,9
  131.   je    @StringOut
  132.   cmp   ah,$40
  133.   je    @BlockOut
  134.   jmp   @JmpOld21
  135.  
  136. @TtyOut:                        {BIOS function 0E}
  137.   push  ax
  138.   call  @WriteChar
  139.   pop   ax
  140.   iret
  141.  
  142. @MultOut:                       {BIOS functions 09 and 0A}
  143.   push  ax
  144.   push  cx
  145. @MultOut1:
  146.   call  @WriteChar
  147.   loop  @MultOut1
  148.   pop   cx
  149.   pop   ax
  150.   iret
  151.  
  152. @SetCurPos:                     {BIOS function 02}
  153.   push  ax
  154.   push  ds
  155.   push  cs
  156.   pop   ds                      {DS = CS}
  157.   cmp   dl,ds:[WindLoX]
  158.   jae   @SetOKCLo
  159.   mov   dl,ds:[WindLoX]
  160. @SetOKCLo:
  161.   cmp   dl,ds:[WindHiX]
  162.   jbe   @SetOKCHi
  163.   mov   dl,ds:[WindHiX]
  164. @SetOKCHi:
  165.   cmp   dh,ds:[WindLoY]
  166.   jae   @SetOKRLo
  167.   mov   dh,ds:[WindLoY]
  168. @SetOKRLo:
  169.   cmp   dh,ds:[WindHiY]
  170.   jbe   @SetOKRHi
  171.   mov   dh,ds:[WindHiY]
  172. @SetOKRHi:
  173.   mov   ds:[WindPosX],dx
  174.   pop   ds
  175.   pop   ax
  176.   jmp   @JmpOld10
  177.  
  178. @DirectOut:                     {DOS function 6}
  179.   cmp   dl,$FF                  {Console input?}
  180.   je    @JmpOld21
  181.  
  182. @DispOut:                       {DOS function 2}
  183.   push  ax
  184.   mov   al,dl
  185.   call  @WriteChar
  186.   pop   ax
  187.   clc
  188.   retf  2
  189.  
  190. @StringOut:                     {DOS function 9}
  191.   push  ax
  192.   push  bx
  193.   mov   bx,dx
  194. @StringOut1:
  195.   mov   al,[bx]                 {DS:BX points to string to display}
  196.   cmp   al,'$'
  197.   je    @StringOut2
  198.   call  @WriteChar
  199.   inc   bx
  200.   jmp   @StringOut1
  201. @StringOut2:
  202.   pop   bx
  203.   pop   ax
  204.   clc
  205.   retf  2
  206.  
  207. @BlockOut:                      {DOS function $40}
  208.   cmp   bx,1                    {to StdOut?}
  209.   je    @BlockOut1
  210.   cmp   bx,2                    {to StdErr?}
  211.   je    @BlockOut1
  212.   jmp   @JmpOld21
  213. @BlockOut1:
  214.   jcxz  @BlockOut3              {Anything to write?}
  215.   push  ax
  216.   push  bx
  217.   push  cx
  218.   mov   bx,dx
  219. @BlockOut2:
  220.   mov   al,[bx]                 {DS:BX points to character to display}
  221.   call  @WriteChar
  222.   inc   bx
  223.   loop  @BlockOut2
  224.   pop   bx
  225.   pop   bx
  226.   pop   ax
  227. @BlockOut3:
  228.   mov   ax,cx                   {Wrote all the bytes}
  229.   clc
  230.   retf  2
  231.  
  232. {This routine writes each character within the window}
  233. @WriteChar:
  234.   push  bp                      {Some int 10 handlers trash BP}
  235.   push  bx
  236.   push  cx
  237.   push  dx
  238.   push  ds
  239.   push  cs
  240.   pop   ds                      {DS = CS}
  241.  
  242.   mov   dx,ds:[WindPosX]        {Current cursor pos in DX}
  243.  
  244.   cmp   al,7
  245.   je    @WriteDone              {Ignore bell character}
  246.   cmp   al,8
  247.   je    @WriteBS
  248.   cmp   al,9
  249.   je    @WriteTab
  250.   cmp   al,10
  251.   je    @WriteLF
  252.   cmp   al,13
  253.   je    @WriteCR
  254.   call  @WriteNormal            {Write normal character}
  255.  
  256. @WriteDone:
  257.   pop   ds
  258.   pop   dx
  259.   pop   cx
  260.   pop   bx
  261.   pop   bp
  262.   ret
  263.  
  264. {Special case for carriage return}
  265. @WriteCR:
  266.   mov   dl,ds:[WindLoX]
  267.   call  @SetCursor
  268.   jmp   @WriteDone
  269.  
  270. {Special case for line feed}
  271. @WriteLF:
  272.   cmp   dh,ds:[WindHiY]
  273.   jb    @WriteLF1
  274.   call  @ScrollUp
  275.   jmp   @WriteDone
  276. @WriteLF1:
  277.   inc   dh
  278.   call  @SetCursor
  279.   jmp   @WriteDone
  280.  
  281. {Special case for tab}
  282. @WriteTab:
  283.   mov   cl,dl
  284.   sub   cl,ds:[WindLoX]
  285.   add   cl,8
  286.   and   cl,$F8
  287.   add   cl,ds:[WindLoX]
  288.   sub   cl,dl
  289.   xor   ch,ch
  290. @WriteTab1:
  291.   mov   al,' '
  292.   push  cx
  293.   call  @WriteNormal
  294.   pop   cx
  295.   loop  @WriteTab1
  296.   jmp   @WriteDone
  297.  
  298. {Special case for backspace}
  299. @WriteBS:
  300.   cmp   dl,ds:[WindLoX]         {At left window edge?}
  301.   jbe   @WriteDone              {Done if so}
  302.   dec   dl                      {Cursor left one}
  303.   xor   bh,bh
  304.   mov   ah,2                    {Position cursor}
  305.   pushf
  306.   call  dword ptr ds:[Int10Patch]
  307.   mov   ds:[WindPosX],dx
  308.   mov   cx,1
  309.   mov   bl,ds:[WindAttr]
  310.   mov   ax,$0920                {Write a space}
  311.   pushf
  312.   call  dword ptr ds:[Int10Patch]
  313.   jmp   @WriteDone
  314.  
  315. {Write normal character and scroll if needed}
  316. @WriteNormal:
  317.   mov   cx,1
  318.   mov   bl,ds:[WindAttr]
  319.   xor   bh,bh
  320.   mov   ah,9                    {Write the character}
  321.   pushf
  322.   call  dword ptr ds:[Int10Patch]
  323.   cmp   dl,ds:[WindHiX]         {Beyond right border?}
  324.   jb    @IncCol
  325.   cmp   dh,ds:[WindHiY]         {Room for CR/LF?}
  326.   jb    @IncRow
  327.   call  @ScrollUp
  328.   dec   dh                      {Compensate for inc to follow}
  329. @IncRow:
  330.   inc   dh                      {Next row}
  331.   mov   dl,ds:[WindLoX]         {First col}
  332.   dec   dl                      {Compensate for inc to follow}
  333. @IncCol:
  334.   inc   dl                      {Next column}
  335.  
  336. {Position cursor and update saved position}
  337. @SetCursor:
  338.   xor   bh,bh
  339.   mov   ah,2
  340.   pushf
  341.   call  dword ptr ds:[Int10Patch]
  342.   mov   ds:[WindPosX],dx
  343.   ret
  344.  
  345. {Scroll window up one line}
  346. @ScrollUp:
  347.   mov   ax,$0601
  348.   mov   cx,ds:[WindLoX]
  349.   mov   dx,ds:[WindHiX]
  350.   mov   bh,ds:[WindAttr]
  351.   pushf
  352.   call  dword ptr ds:[Int10Patch]
  353.   ret
  354. end;
  355.  
  356. {Keep immediately after IntHandlers to measure size}
  357. procedure IntHandlersEnd; near; assembler;
  358. asm
  359. end;
  360.  
  361. function ExecWindow(Command : String;
  362.                     UseSecond : Boolean;
  363.                     Xlo, Ylo, Xhi, Yhi : Byte;
  364.                     Attr : Byte) : Integer;
  365. type
  366.   {Types mirror the data at the start of int handler above}
  367.   CoordRec =
  368.     record
  369.       case integer of
  370.         0 : (X, Y: Byte);
  371.         1 : (XY : Word)
  372.     end;
  373.   WindRec =
  374.     record
  375.       WindLo   : CoordRec;
  376.       WindHi   : CoordRec;
  377.       WindPos  : CoordRec;
  378.       WindAttr : Byte;
  379.     end;
  380. var
  381.   OldRealMode10 : Pointer;
  382.   OldRealMode21 : Pointer;
  383.   OldRealMode29 : Pointer;
  384.   DosMemBlock :
  385.     record
  386.       Sele, Segm : Word;
  387.     end;
  388.   Status : Integer;
  389.   BlankPos : Word;
  390.   PathName : string[127];
  391.   CommandTail : string[127];
  392.  
  393.   procedure InitializeCursor(var Win : WindRec); assembler;
  394.     {-Assure cursor is in window}
  395.   asm
  396.     {Get pointer to WindRec}
  397.     les   di,Win
  398.  
  399.     {Get cursor pos}
  400.     mov   ah,3
  401.     xor   bh,bh
  402.     int   $10
  403.  
  404.     {Assure it's within window}
  405.     mov   cx,WindRec(es:[di]).WindLo.XY
  406.     cmp   dh,ch       {Row above minimum?}
  407.     jae   @OkXLo      {Jump if so}
  408.     mov   dh,ch
  409. @OkXLo:
  410.     cmp   dl,cl       {Col above minimum?}
  411.     jae   @OkYLo      {Jump if so}
  412.     mov   dl,cl
  413. @OkYLo:
  414.     mov   cx,WindRec(es:[di]).WindHi.XY
  415.     cmp   dh,ch       {Row below maximum?}
  416.     jbe   @OkXHi      {Jump if so}
  417.     mov   dh,ch
  418. @OkXHi:
  419.     cmp   dl,cl       {Col below maximum?}
  420.     jbe   @OkYHi      {Jump if so}
  421.     mov   dl,cl
  422. @OkYHi:
  423.     {Save current position}
  424.     mov   WindRec(es:[di]).WindPos.XY,dx
  425.  
  426.     {Position cursor}
  427.     mov   ah,2
  428.     xor   bh,bh
  429.     int   $10
  430.   end;
  431.  
  432.   function SetupHandlers : Integer;
  433.   var
  434.     Size : Word;
  435.     Win : WindRec;
  436.   begin
  437.     {Allocate a block of real mode memory}
  438.     Size := ofs(IntHandlersEnd)-ofs(IntHandlers);
  439.     LongInt(DosMemBlock) := GlobalDosAlloc(Size);
  440.     if LongInt(DosMemBlock) = 0 then begin
  441.       SetupHandlers := Integer($8008);
  442.       Exit;
  443.     end;
  444.     {Copy the int handlers to real memory}
  445.     move(@IntHandlers^, Mem[DosMemBlock.Sele:0], Size);
  446.     {Patch the old real mode vectors into the handler}
  447.     GetRealModeIntVector($21, OldRealMode21);
  448.     move(OldRealMode21, Mem[DosMemBlock.Sele:Int21Patch], 4);
  449.     GetRealModeIntVector($10, OldRealMode10);
  450.     move(OldRealMode10, Mem[DosMemBlock.Sele:Int10Patch], 4);
  451.     GetRealModeIntVector($29, OldRealMode29);
  452.     {Put window data into the handler}
  453.     with Win do begin
  454.       WindLo.X := Xlo-1;
  455.       WindLo.Y := Ylo-1;
  456.       WindHi.X := Xhi-1;
  457.       WindHi.Y := Yhi-1;
  458.       WindAttr := Attr;
  459.     end;
  460.     InitializeCursor(Win);
  461.     move(Win, Mem[DosMemBlock.Sele:0], SizeOf(WindRec));
  462.     {Activate the handlers}
  463.     SetRealModeIntVector($21, Ptr(DosMemBlock.Segm, Int21Entry));
  464.     SetRealModeIntVector($10, Ptr(DosMemBlock.Segm, Int10Entry));
  465.     SetRealModeIntVector($29, Ptr(DosMemBlock.Segm, Int29Entry));
  466.     SetupHandlers := 0;
  467.   end;
  468.  
  469.   procedure ShutdownHandlers;
  470.   var
  471.     Sele : Word;
  472.   begin
  473.     SetRealModeIntVector($21, OldRealMode21);
  474.     SetRealModeIntVector($10, OldRealMode10);
  475.     SetRealModeIntVector($29, OldRealMode29);
  476.     Sele := GlobalDosFree(DosMemBlock.Sele);
  477.   end;
  478.  
  479. begin
  480.   {Validate window}
  481.   if (Xlo > Xhi) or (Ylo > Yhi) or (Xlo < 1) or (Ylo < 1) then begin
  482.     ExecWindow := 99;
  483.     Exit;
  484.   end;
  485.  
  486.   {Install the int 21 handler in real mode memory}
  487.   Status := SetupHandlers;
  488.   if Status = 0 then begin
  489.  
  490.     {Parse command into parts for Exec}
  491.     if Command = '' then
  492.       UseSecond := True;
  493.     CommandTail := '';
  494.     if not UseSecond then begin
  495.       {Command is assumed to be a full pathname for a program}
  496.       BlankPos := Pos(' ', Command);
  497.       if BlankPos = 0 then
  498.         PathName := Command
  499.       else begin
  500.         CommandTail := Copy(Command, BlankPos, Length(Command));
  501.         PathName := Copy(Command, 1, BlankPos-1);
  502.       end;
  503.     end else begin
  504.       {Pathname is the full pathname for COMMAND.COM}
  505.       PathName := GetEnv('COMSPEC');
  506.       if Command <> '' then
  507.         CommandTail := '/C '+Command;
  508.     end;
  509.  
  510.     {Perform the exec}
  511.     SwapVectors;
  512.     Exec(PathName, CommandTail);
  513.     Status := DosError;
  514.     {Account for bug in some DPMI servers}
  515.     if Status = $4B00 then
  516.       Status := 0;
  517.     SwapVectors;
  518.  
  519.     {Remove the int 21 handler from real mode memory}
  520.     ShutdownHandlers;
  521.   end;
  522.   ExecWindow := Status;
  523. end;
  524.  
  525. {$ELSE}  {DOS real mode version----------------------------------------------}
  526.  
  527. uses
  528.   Dos, OpDos;
  529.  
  530. type
  531.   ByteCast =
  532.     record
  533.       LoB, HiB : Byte;
  534.     end;
  535.  
  536. var
  537.   CurInt21 : Pointer;
  538.   CurInt10 : Pointer;
  539.   CurInt29 : Pointer;
  540.   WindPos : Word;
  541.   WindLo : Word;
  542.   WindHi : Word;
  543.   WindAttr : Byte;
  544.  
  545.   {$L EXECWIN}
  546.   procedure SetCsInts; external;
  547.   procedure NewInt21; external;
  548.   procedure NewInt10; external;
  549.   procedure NewInt29; external;
  550.  
  551.   {$IFDEF Ver60}
  552.     {$DEFINE Fix21}
  553.   {$ENDIF}
  554.   {$IFDEF Ver70}
  555.     {$DEFINE Fix21}
  556.   {$ENDIF}
  557.  
  558.   function ExecWindow(Command : string; UseSecond : Boolean;
  559.                       Xlo, Ylo, Xhi, Yhi : Byte;
  560.                       Attr : Byte) : Integer;
  561.     {-Exec a program in a window}
  562. {$IFDEF Fix21}
  563.   var
  564.     TmpInt21 : Pointer;
  565. {$ENDIF}
  566.   begin
  567.     {Validate window}
  568.     if (Xlo > Xhi) or (Ylo > Yhi) or (Xlo < 1) or (Ylo < 1) then begin
  569.       ExecWindow := 99;
  570.       Exit;
  571.     end;
  572.  
  573.     {Store global copies of window data for interrupt handler}
  574.     WindAttr := Attr;
  575.     ByteCast(WindLo).LoB := Xlo-1;
  576.     ByteCast(WindLo).HiB := Ylo-1;
  577.     ByteCast(WindHi).LoB := Xhi-1;
  578.     ByteCast(WindHi).HiB := Yhi-1;
  579.  
  580.     {Assure cursor is in window}
  581.     inline
  582.     (
  583.      {;get cursor pos}
  584.      $B4/$03/                     {  mov ah,3}
  585.      $30/$FF/                     {  xor bh,bh}
  586.      $CD/$10/                     {  int $10}
  587.      {;assure it's within window}
  588.      $8B/$0E/>WindLo/             {  mov cx,[>windlo]}
  589.      $38/$EE/                     {  cmp dh,ch ;row above minimum?}
  590.      $73/$02/                     {  jae okxlo ;jump if so}
  591.      $88/$EE/                     {  mov dh,ch}
  592.      {okxlo:}
  593.      $38/$CA/                     {  cmp dl,cl ;col above minimum?}
  594.      $73/$02/                     {  jae okylo ;jump if so}
  595.      $88/$CA/                     {  mov dl,cl}
  596.      {okylo:}
  597.      $8B/$0E/>WindHi/             {  mov cx,[>windhi]}
  598.      $38/$EE/                     {  cmp dh,ch ;row below maximum?}
  599.      $76/$02/                     {  jbe okxhi ;jump if so}
  600.      $88/$EE/                     {  mov dh,ch}
  601.      {okxhi:}
  602.      $38/$CA/                     {  cmp dl,cl ;col below maximum?}
  603.      $76/$02/                     {  jbe okyhi ;jump if so}
  604.      $88/$CA/                     {  mov dl,cl}
  605.      {okyhi:}
  606.      $89/$16/>WindPos/            {  mov [>windpos],dx ;save current position}
  607.      {;position cursor}
  608.      $B4/$02/                     {  mov ah,2}
  609.      $30/$FF/                     {  xor bh,bh}
  610.      $CD/$10);                    {  int $10}
  611.  
  612.     {Take over interrupt}
  613.     GetIntVec($21, CurInt21);
  614.     GetIntVec($10, CurInt10);
  615.     GetIntVec($29, CurInt29);
  616.     SetCsInts;
  617.     SetIntVec($21, @NewInt21);
  618.     SetIntVec($10, @NewInt10);
  619.     SetIntVec($29, @NewInt29);
  620.  
  621.   {$IFDEF Fix21}
  622.     {Prevent SwapVectors from undoing our int21 change}
  623.     TmpInt21 := SaveInt21;
  624.     SaveInt21 := @NewInt21;
  625.   {$ENDIF}
  626.  
  627.     {Exec the program}
  628.     ExecWindow := ExecDos(Command, UseSecond, NoExecDosProc);
  629.  
  630.   {$IFDEF Fix21}
  631.     SaveInt21 := TmpInt21;
  632.   {$ENDIF}
  633.  
  634.     {Restore interrupt}
  635.     SetIntVec($21, CurInt21);
  636.     SetIntVec($10, CurInt10);
  637.     SetIntVec($29, CurInt29);
  638.   end;
  639. {$ENDIF}
  640.  
  641. end.
  642.